/*************************************************************************
 *
 *   file		: OpenLock.cs
 *   copyright		: (C) The WCell Team
 *   email		: info@wcell.org
 *   last changed	: $LastChangedDate: 2009-06-20 08:52:49 +0100 (Sat, 20 Jun 2009) $
 *   last author	: $LastChangedBy: dominikseifert $
 *   revision		: $Rev: 1010 $
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *************************************************************************/

using NLog;
using WCell.Constants;
using WCell.Constants.GameObjects;
using WCell.Constants.Skills;
using WCell.Constants.Spells;
using WCell.RealmServer.Entities;
using WCell.RealmServer.Misc;
using WCell.RealmServer.Skills;
using WCell.Util;

namespace WCell.RealmServer.Spells.Effects
{
	/// <summary>
	/// Tries to open a GameObject or Item or disarm a trap
	/// </summary>
	public class OpenLockEffectHandler : SpellEffectHandler
	{
		private static readonly Logger log = LogManager.GetCurrentClassLogger();

		ILockable lockable;
		LockOpeningMethod method;
		Skill skill;

		public OpenLockEffectHandler(SpellCast cast, SpellEffect effect)
			: base(cast, effect)
		{
		}

		public override void Initialize(ref SpellFailedReason failReason)
		{
			if (m_cast.Selected != null)
			{
				lockable = m_cast.Selected as GameObject;
			}
			else
			{
				lockable = m_cast.UsedItem;
			}

			if (lockable == null)
			{
				failReason = SpellFailedReason.BadTargets;
			}
			else
			{
				var lck = lockable.Lock;

				if (lck == null)
				{
					log.Warn("Using OpenLock on object without Lock: " + lockable);
					failReason = SpellFailedReason.Error;
					return;
				}

			    if (!lck.IsUnlocked)
			    {
			    	var type = (LockInteractionType) Effect.MiscValue;
			    	if (lck.Keys.Length > 0 && m_cast.CasterItem != null)
			    	{
			    		if (!lck.Keys.Contains(key => key.KeyId == m_cast.CasterItem.Template.ItemId))
			    		{
			    			failReason = SpellFailedReason.ItemNotFound;
							return;
			    		}
			    	}
			    	else if (!lck.Supports(type))
			    	{
			    		failReason = SpellFailedReason.BadTargets;
			    		return;
			    	}

					if (type != LockInteractionType.None)
					{
						foreach (var openingMethod in lck.OpeningMethods)
						{
							if (openingMethod.InteractionType == type)
							{
								if (openingMethod.RequiredSkill != SkillId.None)
								{
									var chr = m_cast.CasterChar;
									if (chr != null)
									{
										skill = chr.Skills[openingMethod.RequiredSkill];
										if (skill == null || skill.ActualValue < openingMethod.RequiredSkillValue)
										{
											failReason = SpellFailedReason.MinSkill;
										}
									}
								}
								method = openingMethod;
								break;
							}
						}

						if (method == null)
						{
							// we are using the wrong kind of spell on the target
							failReason = SpellFailedReason.BadTargets;
						}
					}
			    }

				if (failReason != SpellFailedReason.Ok)
				{
					// spell failed
					if (lockable is GameObject && ((GameObject)lockable).Entry.IsConsumable)
					{
						// re-enable GO
						((GameObject)lockable).State = GameObjectState.Enabled;
					}
				}
			}
		}

		public override void Apply()
		{
			if (skill != null)
			{
				// check if the skill works
				// TODO: Lockable items need skill-color information
				var diff = skill.ActualValue - method.RequiredSkillValue;

				// assume grey at a difference of 50
				if (diff < 50)
				{
					var chance = (diff * 2) + 1;
					if (chance <= Utility.Random(0, 100))
					{
						// failed
						m_cast.Cancel(SpellFailedReason.TryAgain);
						return;
					}

				    // skill gain?
				    skill.Gain(80 - (int)chance, 1);
				}
			}

			LockEntry.Handle(m_cast.CasterChar, lockable, method != null ? method.InteractionType : LockInteractionType.None);
		}

		protected override void Apply(WorldObject target)
		{
			// this is not a multiple target effect
		}

		public override ObjectTypes CasterType
		{
			get
			{
				return ObjectTypes.Player;
			}
		}

		public override bool HasOwnTargets
		{
			get
			{
				return false;
			}
		}
	}
}
